"""
DeepSeek API内容生成服务
"""
import httpx
import asyncio
from typing import Dict, Any, Optional
from app.config import settings, app_config
from app.utils.logger import logger
from app.utils.exceptions import ContentGenerationError


class DeepSeekService:
    """DeepSeek API服务类"""
    
    def __init__(self):
        self.api_key = settings.deepseek_api_key
        self.base_url = settings.deepseek_base_url
        self.config = app_config.deepseek
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
    
    async def _make_request_non_reasoning(self, prompt: str, max_tokens: int = None) -> str:
        """发送请求到DeepSeek API（非思考模式）"""
        url = f"{self.base_url}/v1/chat/completions"
        
        data = {
            "model": "deepseek-chat",  # 使用非思考模式
            "messages": [
                {"role": "user", "content": prompt}
            ],
            "max_tokens": max_tokens or 200,  # 非思考模式需要的token更少
            "temperature": self.config["temperature"],
            "top_p": self.config["top_p"],
            "stream": False
        }
        
        try:
            async with httpx.AsyncClient(timeout=60.0) as client:  # 非思考模式超时时间更短
                logger.info(f"发送DeepSeek API请求（非思考模式）: {url}")
                logger.info(f"请求数据: model={data['model']}, max_tokens={data['max_tokens']}")
                
                response = await client.post(url, json=data, headers=self.headers)
                
                logger.info(f"DeepSeek API响应状态: {response.status_code}")
                
                response.raise_for_status()
                
                result = response.json()
                logger.info(f"DeepSeek API响应结构: {list(result.keys())}")
                
                if "choices" not in result:
                    logger.error(f"DeepSeek API响应缺少choices字段: {result}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：缺少choices字段")
                
                if len(result["choices"]) == 0:
                    logger.error(f"DeepSeek API响应choices为空: {result}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：choices数组为空")
                
                choice = result["choices"][0]
                if "message" not in choice:
                    logger.error(f"DeepSeek API响应缺少message字段: {choice}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：缺少message字段")
                
                content = choice["message"].get("content", "")
                
                if not content:
                    logger.error(f"DeepSeek API返回内容为空: {choice}")
                    raise ContentGenerationError("DeepSeek API 返回内容为空")
                
                content = content.strip()
                if not content:
                    logger.error(f"DeepSeek API返回内容去除空格后为空")
                    raise ContentGenerationError("DeepSeek API 返回内容去除空格后为空")
                
                logger.info(f"DeepSeek API 调用成功（非思考模式），返回内容长度: {len(content)}")
                return content
                    
        except httpx.HTTPStatusError as e:
            error_detail = f"HTTP {e.response.status_code}: {e.response.text[:500]}"
            logger.error(f"DeepSeek API HTTP错误: {error_detail}")
            raise ContentGenerationError(f"DeepSeek API调用失败: HTTP {e.response.status_code}")
        except httpx.TimeoutException as e:
            logger.error(f"DeepSeek API 超时: {str(e)}")
            raise ContentGenerationError(f"DeepSeek API超时: {e}")
        except httpx.RequestError as e:
            logger.error(f"DeepSeek API 请求错误: {str(e)}")
            raise ContentGenerationError(f"DeepSeek API请求错误: {e}")
        except Exception as e:
            import traceback
            error_traceback = traceback.format_exc()
            logger.error(f"DeepSeek API 调用异常详情:")
            logger.error(f"错误类型: {type(e).__name__}")
            logger.error(f"错误信息: {str(e)}")
            logger.error(f"请求URL: {url}")
            logger.error(f"请求数据: {data}")
            logger.error(f"请求头: {self.headers}")
            logger.error(f"完整堆栈信息:\n{error_traceback}")
            
            # 检查是否是内容为空的特殊情况
            if "content" in str(e).lower():
                logger.error("疑似API返回内容为空或格式异常")
                raise ContentGenerationError(f"DeepSeek API返回内容为空: {e}")
            
            raise ContentGenerationError(f"DeepSeek API调用异常: {e}")

    async def _make_request(self, prompt: str, max_tokens: int = None) -> str:
        """发送请求到DeepSeek API"""
        url = f"{self.base_url}/v1/chat/completions"
        
        data = {
            "model": self.config["model"],
            "messages": [
                {"role": "user", "content": prompt}
            ],
            "max_tokens": max_tokens or self.config["max_tokens"],
            "temperature": self.config["temperature"],
            "top_p": self.config["top_p"],
            "stream": False
        }
        
        try:
            async with httpx.AsyncClient(timeout=300.0) as client:
                logger.info(f"发送DeepSeek API请求: {url}")
                logger.info(f"请求数据: model={data['model']}, max_tokens={data['max_tokens']}")
                
                response = await client.post(url, json=data, headers=self.headers)
                
                logger.info(f"DeepSeek API响应状态: {response.status_code}")
                
                response.raise_for_status()
                
                result = response.json()
                logger.info(f"DeepSeek API响应结构: {list(result.keys())}")
                
                if "choices" not in result:
                    logger.error(f"DeepSeek API响应缺少choices字段: {result}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：缺少choices字段")
                
                if len(result["choices"]) == 0:
                    logger.error(f"DeepSeek API响应choices为空: {result}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：choices数组为空")
                
                choice = result["choices"][0]
                if "message" not in choice:
                    logger.error(f"DeepSeek API响应缺少message字段: {choice}")
                    raise ContentGenerationError("DeepSeek API 返回格式异常：缺少message字段")
                
                message = choice["message"]
                content = message.get("content", "")
                reasoning_content = message.get("reasoning_content", "")
                finish_reason = choice.get("finish_reason", "")
                
                # 如果content为空，但reasoning_content有内容，尝试从reasoning_content中提取
                if not content or not content.strip():
                    if reasoning_content:
                        logger.warning(f"DeepSeek API返回content为空，但reasoning_content有内容，finish_reason={finish_reason}")
                        logger.warning(f"尝试从reasoning_content中提取最终答案")
                        
                        # 从reasoning_content中查找可能的最终答案
                        # 通常最终答案在reasoning_content的末尾
                        reasoning_text = reasoning_content
                        
                        # 尝试提取最后一段作为答案
                        # 查找"最终"、"标题"、"答案"等关键词后的内容
                        import re
                        # 查找可能的标题模式（通常在最后）
                        title_patterns = [
                            r'最终标题[：:]\s*([^\n]+)',
                            r'标题[：:]\s*([^\n]+)',
                            r'答案[：:]\s*([^\n]+)',
                            r'["""]([^"""]+)["""]',  # 引号中的内容
                            r'「([^」]+)」',  # 中文引号
                            r'【([^】]+)】',  # 中文方括号
                        ]
                        
                        extracted_content = None
                        for pattern in title_patterns:
                            matches = re.findall(pattern, reasoning_text)
                            if matches:
                                # 取最后一个匹配
                                extracted_content = matches[-1].strip()
                                if extracted_content and len(extracted_content) > 0:
                                    logger.info(f"从reasoning_content中提取到内容: {extracted_content[:50]}...")
                                    content = extracted_content
                                    break
                        
                        # 如果还是没找到，尝试取reasoning_content的最后几行
                        if not content or not content.strip():
                            lines = reasoning_text.split('\n')
                            # 从后往前查找，找到第一个非空的、看起来像答案的行
                            for line in reversed(lines):
                                line = line.strip()
                                if line and len(line) > 0:
                                    # 跳过明显的思考过程标记
                                    if not any(marker in line for marker in ['思考', '分析', '检查', '确保', '验证', '优化']):
                                        if len(line) <= 100:  # 标题通常不会太长
                                            content = line
                                            logger.info(f"从reasoning_content末尾提取到内容: {content}")
                                            break
                        
                        # 如果还是没找到，使用reasoning_content的最后一段
                        if not content or not content.strip():
                            # 取最后200个字符作为备选
                            last_part = reasoning_text[-200:].strip()
                            if last_part:
                                # 尝试提取最后一句完整的话
                                sentences = re.split(r'[。！？\n]', last_part)
                                if sentences:
                                    content = sentences[-1].strip()
                                    if not content:
                                        content = last_part.strip()
                                    logger.info(f"使用reasoning_content的最后部分: {content[:50]}...")
                
                # 如果content仍然为空，抛出错误
                if not content or not content.strip():
                    logger.error(f"DeepSeek API返回内容为空: choice={choice}, finish_reason={finish_reason}")
                    if finish_reason == "length":
                        logger.error("达到最大token限制，建议增加max_tokens")
                    raise ContentGenerationError("DeepSeek API 返回内容为空")
                
                content = content.strip()
                if not content:
                    logger.error(f"DeepSeek API返回内容去除空格后为空")
                    raise ContentGenerationError("DeepSeek API 返回内容去除空格后为空")
                
                logger.info(f"DeepSeek API 调用成功，返回内容长度: {len(content)}")
                return content
                    
        except httpx.HTTPStatusError as e:
            error_detail = f"HTTP {e.response.status_code}: {e.response.text[:500]}"
            logger.error(f"DeepSeek API HTTP错误: {error_detail}")
            raise ContentGenerationError(f"DeepSeek API调用失败: HTTP {e.response.status_code}")
        except httpx.TimeoutException as e:
            logger.error(f"DeepSeek API 超时: {str(e)}")
            raise ContentGenerationError(f"DeepSeek API超时: {e}")
        except httpx.RequestError as e:
            logger.error(f"DeepSeek API 请求错误: {str(e)}")
            raise ContentGenerationError(f"DeepSeek API请求错误: {e}")
        except Exception as e:
            import traceback
            error_traceback = traceback.format_exc()
            logger.error(f"DeepSeek API 调用异常详情:")
            logger.error(f"错误类型: {type(e).__name__}")
            logger.error(f"错误信息: {str(e)}")
            logger.error(f"请求URL: {url}")
            logger.error(f"请求数据: {data}")
            logger.error(f"请求头: {self.headers}")
            logger.error(f"完整堆栈信息:\n{error_traceback}")
            
            # 检查是否是内容为空的特殊情况
            if "content" in str(e).lower():
                logger.error("疑似API返回内容为空或格式异常")
                raise ContentGenerationError(f"DeepSeek API返回内容为空: {e}")
            
            raise ContentGenerationError(f"DeepSeek API调用异常: {e}")
    
    async def generate_title(self, theme: str, keywords: list = None) -> str:
        """生成文章标题"""
        keywords_str = "、".join(keywords) if keywords else ""
        keywords_part = f"，相关关键词：{keywords_str}" if keywords_str else ""
        
        prompt = f"""
请根据主题'{theme}'{keywords_part}生成一个吸引人的微信公众号文章标题。

要求：
1. 标题不超过60个中文字符
2. 具有吸引力和点击欲望
3. 符合微信公众号文章标题的特点
4. 内容积极正面
5. 直接返回标题，不要其他说明文字

主题：{theme}
"""
        
        # 对于reasoner模型，需要更多token用于推理，增加到1000
        title_max_tokens = self.config.get("title_max_tokens", 500)
        if "reasoner" in self.config.get("model", "").lower():
            title_max_tokens = max(title_max_tokens, 1000)
            logger.info(f"使用reasoner模型，增加max_tokens到{title_max_tokens}")
        
        title = await self._make_request(prompt, max_tokens=title_max_tokens)
        
        # 验证和清理标题
        if not title or not title.strip():
            logger.error("生成的标题为空")
            raise ContentGenerationError("生成的标题为空")
        
        title = title.strip()
        
        # 验证标题长度
        if len(title) > app_config.generation["title_max_length"]:
            logger.warning(f"生成的标题过长({len(title)}字符)，重新生成")
            # 重新生成更短的标题
            prompt += f"\n注意：标题必须严格控制在{app_config.generation['title_max_length']}个中文字符以内"
            title = await self._make_request(prompt, max_tokens=title_max_tokens)
            title = title.strip() if title else ""
            
            # 如果还是过长，直接截断
            if len(title) > app_config.generation["title_max_length"]:
                title = title[:app_config.generation["title_max_length"] - 3] + "..."
        
        # 最后一次验证
        if not title or not title.strip():
            raise ContentGenerationError("无法生成有效标题")
        
        logger.info(f"生成标题成功: {title}")
        return title
    
    async def generate_summary(self, title: str, theme: str) -> str:
        """生成文章摘要"""
        prompt = f"""
请为标题'{title}'写一个简洁的文章摘要。

要求：
1. 摘要不超过100个中文字符
2. 突出文章核心价值
3. 吸引读者阅读兴趣
4. 语言简洁有力
5. 直接返回摘要，不要其他说明文字

标题：{title}
主题：{theme}
"""
        
        summary = await self._make_request(prompt, max_tokens=self.config.get("summary_max_tokens", 800))
        
        # 验证和清理摘要
        if summary:
            summary = summary.strip()
        else:
            summary = ""
        
        # 验证摘要长度
        if summary and len(summary) > app_config.generation["summary_max_length"]:
            logger.warning(f"生成的摘要过长({len(summary)}字符)，重新生成")
            prompt += f"\n注意：摘要必须严格控制在{app_config.generation['summary_max_length']}个中文字符以内"
            summary = await self._make_request(prompt, max_tokens=self.config.get("summary_max_tokens", 800))
            summary = summary.strip() if summary else ""
            
            # 如果还是过长，直接截断
            if len(summary) > app_config.generation["summary_max_length"]:
                summary = summary[:app_config.generation["summary_max_length"] - 3] + "..."
        
        logger.info(f"生成摘要成功: {summary[:50]}...")
        return summary
    
    async def generate_content(self, title: str, theme: str, target_length: int = None) -> str:
        """生成文章正文"""
        min_length = target_length or app_config.generation["content_min_length"]
        
        prompt = f"""
请以'{title}'为标题写一篇符合微信公众号原创要求的文章。

【重要】必须严格遵循以下两个核心要求：

1. **符合微信原创要求的完整Markdown格式**：
   - 必须使用标准Markdown语法编写
   - 标题：使用一级标题（# 标题）开头
   - 副标题：使用二级标题（## 标题）划分段落
   - 强调：使用**加粗**标记重点内容
   - 列表：使用"- 项目"格式创建清单
   - 数据：使用【重要数据：具体数据】格式标记关键信息
   - 引用：使用"> 导语内容"格式创建引人注目的导语
   - 内容必须100%原创，避免重复常见表述
   - 语言要生动有趣，符合微信公众号读者阅读习惯

2. **严格包含四个必要模块（标题→导语→正文→结语）**：
   - **标题模块**：必须使用"# {title}"作为文章开头
   - **导语模块**：标题后必须有引用格式的导语（"> 导语内容"），1-2句话快速吸引读者
   - **正文模块**：至少包含3个二级标题（## 标题），每个部分详细阐述不同角度
   - **结语模块**：文章末尾必须有总结段落，呼应主题并给出展望或建议

内容质量要求：
- 字数：不少于{min_length}字，确保内容充实
- 结构：逻辑清晰，段落分明，层次分明
- 语言：简洁生动，通俗易懂，符合中文表达习惯
- 价值：内容实用，有具体的观点、数据、案例或建议
- 原创：完全原创内容，符合微信原创检测标准
- 可读性：适合手机阅读，段落不宜过长

主题：{theme}
标题：{title}
最低字数：{min_length}字

【严格按照以下格式输出，不要添加任何说明文字】：

# {title}

> 精彩的导语内容，快速吸引读者注意力，1-2句话概括文章价值。

## 第一个方面的深入分析

详细内容阐述，必须包含**重点强调**和【重要数据：具体数据】。每段不超过100字，保持手机阅读友好。

### 具体要点

- 要点1：具体内容说明
- 要点2：实用建议或案例
- 要点3：相关数据或观点

## 第二个方面的详细探讨

继续深入分析不同角度，提供**实用价值**和具体建议。

## 第三个方面的实践指导

提供具体的操作方法或实践建议，包含【重要提醒：关键注意事项】。

## 总结与展望

回顾文章要点，给出具体建议或未来展望，与导语形成呼应。

"""
        
        content = await self._make_request(prompt, max_tokens=self.config.get("content_max_tokens", 4000))
        
        # 验证内容不为空
        if not content or not content.strip():
            logger.error("生成的内容为空")
            raise ContentGenerationError("生成的内容为空")
        
        content = content.strip()
        
        # 验证内容长度
        if len(content) < min_length:
            logger.warning(f"生成的内容过短({len(content)}字符)，要求最少{min_length}字符")
            # 补充内容
            supplement_prompt = f"""
请对以下文章进行扩展和丰富，使其达到{min_length}字以上，同时严格保持符合微信原创要求的完整结构：

{content}

【重要】扩展要求必须严格遵循：

1. **维持微信原创标准的Markdown格式**：
   - 保持现有的标准Markdown语法结构
   - 维持# 主标题和## 副标题的层次
   - 继续使用**加粗**强调重点内容
   - 增加更多列表结构（- 项目）
   - 添加更多【重要数据：具体数据】标记的关键信息
   - 确保引用格式（> 导语内容）清晰完整

2. **严格保持四模块完整结构**：
   - **标题模块**：保持"# 标题"格式不变
   - **导语模块**：保持"> 导语内容"引用格式
   - **正文模块**：至少扩展到4个二级标题（## 标题），每部分内容更充实
   - **结语模块**：强化总结段落，与导语形成呼应

3. **内容扩展细节**：
   - 增加更多实用的案例、数据和具体建议
   - 深入阐述重要观点，增加**重点强调**内容
   - 每个二级标题下增加更详细的子内容
   - 添加更多具体的【重要数据：XX%增长】或【关键信息：具体内容】
   - 保持文章的逻辑性和连贯性
   - 确保每段不超过80字，适合手机阅读

4. **质量保证**：
   - 内容必须100%原创，符合微信原创检测标准
   - 语言生动有趣，避免枯燥的理论阐述
   - 提供实用价值，有具体的操作建议或洞察
   - 保持积极正面的基调

【直接返回完整的扩展后Markdown文章，不要添加任何说明文字】
"""
            content = await self._make_request(supplement_prompt, max_tokens=self.config.get("content_max_tokens", 4000))
        
        logger.info(f"生成内容成功，长度: {len(content)}字符")
        return content
    
    async def generate_image_prompts(self, content: str, num_images: int = 3) -> list:
        """根据文章内容生成图片描述提示词"""
        prompt = f"""
请根据以下文章内容，生成{num_images}个适合的图片描述，用于生成配图。

要求：
1. 图片描述要与文章内容相关
2. 适合作为微信公众号文章配图
3. 描述简洁明了，适合AI图像生成
4. 每个描述独立成行
5. 描述要积极正面
6. 适合中文语境

文章内容：
{content[:1000]}...

请生成{num_images}个图片描述：
"""
        
        response = await self._make_request_non_reasoning(prompt, max_tokens=300)
        
        # 解析图片描述
        descriptions = [desc.strip() for desc in response.split('\n') if desc.strip()]
        descriptions = [desc.lstrip('1234567890.- ') for desc in descriptions]
        
        # 确保返回指定数量的描述
        if len(descriptions) < num_images:
            descriptions.extend([descriptions[0]] * (num_images - len(descriptions)))
        elif len(descriptions) > num_images:
            descriptions = descriptions[:num_images]
        
        logger.info(f"生成图片描述成功，数量: {len(descriptions)}")
        return descriptions
    
    async def generate_cover_image_prompt(self, title: str, theme: str) -> str:
        """生成封面图片描述"""
        prompt = f"""
请为微信公众号文章标题"{title}"生成一个封面图片描述。

要求：
1. 图片要简洁美观，符合公众号风格
2. 与标题和主题相关
3. 适合作为文章封面
4. 描述简洁明了，适合AI图像生成
5. 积极正面的视觉效果
6. 直接返回图片描述，不要其他说明文字

标题：{title}
主题：{theme}
"""
        
        description = await self._make_request_non_reasoning(prompt, max_tokens=100)
        logger.info(f"生成封面图片描述成功: {description}")
        return description.strip()


# 全局服务实例
deepseek_service = DeepSeekService()